'''
Copyright 2010 Indra Bagus <indra.bagus@gmail.com> 
    
Licensed under the Apache License, Version 2.0 (the "License"); 
you may not use this file except in compliance with the License. 
You may obtain a copy of the License at 

    http://www.apache.org/licenses/LICENSE-2.0 
    
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, 
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. 
'''



from ManagementMessage import * 
from WimaxStreamHandler import ULMAPHandler, UCDHandler,\
    MMHandlerCollection, RangingRequestHandler
from Tools import BitStream,CalculateHCS, CalculateCRC32OFDMA,\
    IntegerToListInteger
from MACHeader import MACHeaderGeneric
from Type import WmxTLV
from ULMapIE import ULMapIECDMABRequestAndRanging, ULMapIECDMAAllocation,ULMapIEBurstProfile,\
    ULMapIEPowerControl, ULMapIE_AAS_UL, ULMapIEPhysicalModifier,\
    ULMapIEFastTrackingIE,\
    ULMapIEUplinkZone, ULMapIEFastRanging, ULMapIE_CQICHAllocation,\
    ULMapIE_ULPUSCBurstAlloc, ULMapIEULAllocationStart, ULMapIEFastFeedBack
from DLMapIE import DLMapIEBurstProfiles, \
    DLMapIEChannelMeasurement, DLMapIECIDSwitch, DLMapIE_STC_DLZone,\
    DLMapIE_AAS_DL, DLMapIEDataLocAnotherBS, DLMapIEPhysicalModifier,\
    DLMapIEBroadcastCtrlPointer
from Dictionary import MACHeaderDict, DCDDict, UCDDict, DLMAPDict, ULMAPDict



class PDUMMError(Exception):
    '''
    Exception class that will thrown whenever there is Management Message PDU
    Error, such as invalid length, invalid, hcs, invalid crc etc
    '''
    def __init__(self,strerr,macheader):
        assert(isinstance(macheader,MACHeaderGeneric ))
        self.macheader = macheader
        self._strerr = strerr
        
    def __str__(self):
        return self._strerr


class GlobalStaticTable(object):
    cqich_id_size = int()
    
    
class Decoder(object):
    def __init__(self):
        self._calc_crc      = None
        self._current_crc   = None

        
    @property
    def calc_crc(self):
        return self._calc_crc

    
    @calc_crc.setter
    def calc_crc(self,value):
        self._calc_crc = value

        
    @property
    def current_crc(self):
        return self._current_crc

    
    @current_crc.setter
    def current_crc(self,val):
        self._current_crc = val
        
        
    def Decode(self,stream):
        pass



class TLVDecoder(Decoder):
    '''
    Object ini bertanggung jawab menjalankan fungsi 
    TLV decoding. Inputan adalah bitstream object yang berisi raw data 
    sekumpulan data TLV dan ketika fungsi
    decode dijalankan, akan me-return list of TLV
    object
    '''
    def __init__(self,string=''):
        super(TLVDecoder,self).__init__()
        self._string = string

    
    def Decode(self):
        retval  = []
        index   = 0
        while index < len(self._string):
            try :
                tlv =  WmxTLV()
                tlv.Type.val = ord(self._string[index:index+1])
                index +=1
                flag = ord(self._string[index:index+1])
                if((flag & 0x80) != 0):
                    #we have more than 127  bytes of tlv length
                    length_of_tlv_length = flag & 0x7F
                    index = index + 1
                    strlength = self._string[index:index+length_of_tlv_length]
                    iterbyte = iter(strlength)
                    tlv.Length = 0
                    while True:
                        try:
                            tlv.Length = (tlv.Length << 8) | ord(iterbyte.next())
                        except StopIteration:
                            break
                        
                    index += length_of_tlv_length
                else:
                    tlv.Length = ord(self._string[index:index+1])
                    index += 1
                    
                if(len(self._string[index:]) < tlv.Length):
                    raise RuntimeError,'Invalid TLV Length'
                        
                tlv.Value = self._string[index:index+tlv.Length]
                index += tlv.Length
                retval.append(tlv)
            except EOFError:
                break
        return retval
        
    

class MACHeaderGenericDecoder(Decoder):
    def __init__(self,stream = ''):
        super(MACHeaderGenericDecoder,self).__init__()
        self._calculated_hcs = CalculateHCS(stream[0:5])
        self._bitstream = BitStream(stream)

        
    @property    
    def calculated_hcs(self):
        return self._calculated_hcs    

        
    def Decode(self):
        retval                          = MACHeaderGeneric()
        retval.header_type.val          = self._bitstream.read(1)
        retval.header_type.strinfo      = MACHeaderDict.GetHeaderTypeInfo(retval.header_type.val)
        retval.encryption.val           = self._bitstream.read(1)
        retval.encryption.strinfo       = MACHeaderDict.GetEncryptionType(int(retval.encryption))
        retval.encoding_type.reserved               = self._bitstream.read(1)
        retval.encoding_type.arq_feedback.val       = self._bitstream.read(1)
        retval.encoding_type.extended_type.val      = self._bitstream.read(1)
        retval.encoding_type.frag_subheader.val     = self._bitstream.read(1)
        retval.encoding_type.packing_subheader.val  = self._bitstream.read(1)
        retval.encoding_type.fast_feedback_alloc_subheader.val = self._bitstream.read(1)
        retval.esf.val                  = self._bitstream.read(1)
        retval.esf.strinfo              = MACHeaderDict.GetExtSubheaderInfo(int(retval.esf))
        retval.crc_indicator.val        = self._bitstream.read(1)
        retval.crc_indicator.strinfo    = MACHeaderDict.GetCrcIndInfo(int(retval.crc_indicator))
        retval.eks.val                  = self._bitstream.read(2)
        retval.reserved.val             = self._bitstream.read(1)
        retval.pdu_length.val           = self._bitstream.read(11)
        retval.pdu_length.strinfo       = '%d Bytes' % retval.pdu_length.val        
        retval.cid.val                  = self._bitstream.read(16)
        retval.hcs.val                  = self._bitstream.read(8)
        if(retval.hcs.val == self.calculated_hcs):
            retval.hcs.strinfo = 'Valid'
        else:
            retval.hcs.strinfo = 'Invalid'
        return retval
        


class BurstDecoder(Decoder):
    def __init__(self,pduhandler):
        super(BurstDecoder,self).__init__()
        self._pdu_handler = pduhandler
        self._list_pdu  = []

    
    def Decode(self,stringburst):
        pass

            

class PDUDecoder(Decoder):
    '''
    PDU Decoder class adalah base class dari semua PDU Decoder
    '''
    def __init__(self,handler = MMHandlerCollection()):
        '''
        constructor of PDU Decoder object
        @param handler: A reference to MMHandlerCollection 
        '''
        super(Decoder,self).__init__()
        self._mm_cid_list = [0x0000, # Initial Ranging CID
                             0xABAB, # Basic CID
                             0xCDCD, # Primary Mgmt CID
                             0xFEA1, # Multicast CID(0xFEA0 - 0xFEFE)
                             0xFEFF, # AAS Initial Ranging
                             0xFF01, # Multicast polling(0xFF00-0xFFF9)
                             0xFFFA, # Normal Multicast
                             0xFFFB, # Sleep multicast
                             0xFFFC, # Idle Multicast,
                             0xFFFD, # Fragmentatble Multicast
                             0xFFFE, # Padding CID
                             0xFFFF] # Broadcast CID

        
        self._decoder_map = {0:UCDDecoder(handler.ucdhandler),
                             1:DCDDecoder(handler.dcdhandler),
                             2:DLMAPDecoder(handler.dlmaphandler),
                             3:ULMAPDecoder(handler.ulmaphandler),
                             4:RangingRequestDecoder(handler.rngreqhandler)}
        
        self._datahandler = handler.datahandler
        self._enable_crc_filter = False
        
        
    def EnableCRCFilter(self):
        self._enable_crc_filter = True
        
    
    def DisableCRCFilter(self):
        self._enable_crc_filter = False
        
        
    @property
    def basic_cid(self):
        return self._mm_cid_list[1]
    
    
    @basic_cid.setter
    def basic_cid(self,value):
        self._mm_cid_list[1] = value
        
        
    @property
    def primary_cid(self):
        return self._mm_cid_list[2]
    
    
    @primary_cid.setter
    def primary_cid(self,value):
        self._mm_cid_list[2] = value
        
        
    def __get_multicast_cid__(self):
        return self._mm_cid_list[3]
    
    
    def __set_multicast_cid__(self,value):
        self._mm_cid_list[3] = value
    
    multicast_cid = property(__get_multicast_cid__,__set_multicast_cid__)
    
        
    def Decode(self,pdu):
        assert(isinstance(pdu,str))
        # check whether we have generic macheader or not
        macheader_decoder = None
        # check for mac header generic
        if(ord(pdu[0:1])& 0x80 == 0x00):
            macheader_decoder = MACHeaderGenericDecoder(pdu[0:6])
            macheader = macheader_decoder.Decode()
            # check hcs, if it's invalid ended immediately
            if(int(macheader.hcs) != macheader_decoder.calculated_hcs):
                raise PDUMMError('HCS is invalid',macheader) #RuntimeError, 'HCS is invalid'
                
            if (int(macheader.pdu_length) > len(pdu)) :
                raise PDUMMError('PDU Length = %d is not equal with MAC Header' % (len(pdu)),
                                     macheader)
            
            #calculate CRC Indicator if CRC indicator is enabled in MACHeader
            if(int(macheader.crc_indicator) == 1):
                calc_crc = CalculateCRC32OFDMA(pdu[:len(pdu)-4])
                currentcrc = BitStream(pdu[len(pdu)-4:]).read(32)
                if(calc_crc != currentcrc):
                    raise PDUMMError(('0x%8.8X is invalid CRC, it should be 0x%8.8X'%
                                         (currentcrc,calc_crc)),
                                         macheader)
            
            #now check the cid whether we have mm cid
            if int(macheader.cid) in self._mm_cid_list :
                mmtype = ord(pdu[6:7])
                if mmtype not in self._decoder_map:
                    raise PDUMMError('Management Message 0x%2.2X is not implemented' % (mmtype),
                                     macheader)
                
                decoder = self._decoder_map[mmtype]
                if(int(macheader.crc_indicator) == 1):
                    decoder.calc_crc    = calc_crc
                    decoder.current_crc = currentcrc
                    
                decoder.Decode(macheader,pdu)
            
            #we have data cid
            else:
                self._datahandler.OnStartPDU(pdu,macheader)
                self._datahandler.OnEndPDU()
            
        elif(ord(pdu[0:1])& 0x80 == 0x00):
            raise PDUMMError('Signalling Management is not implemented yet207')

       
    

class ULMAPDecoder(Decoder):
    def __init__(self,ulmaphandler):
        super(ULMAPDecoder,self).__init__()
        assert(isinstance(ulmaphandler, ULMAPHandler))
        self._ulmaphandler = ulmaphandler
        self._aas_amc_zone_enabled = False
        self._map_decoder_ie = { 0: self.__decode_fastfeedback_allocation__,
                                12: self.__decode_ranging_opportunity__ ,
                                14: self.__decode_cdma_alloc__,
                                15: self.__decode_ext_uiuc__ }
        
        # map of tuple
        self._ext_uiuc_decoder_map = {
            0x1:self.__reserved__, 
            0x6:self.__reserved__,
            0xB:self.__reserved__,
            0xC:self.__reserved__,
            0xD:self.__reserved__,
            0xE:self.__reserved__,
            0xF:self.__reserved__,
            0x0:self.__decode_power_ctrl__,  
            0x2:self.__decode_aas_ul__,      
            0x3:self.__decode_cqich_alloc__, 
            0x4:self.__decode_ul_zone__,     
            0x5:self.__decode_phy_mod__,     
            0x7:self.__decode_fast_tracking__, 
            0x8:self.__decode_ulpusc_burst_alloc__,  
            0x9:self.__decode_fast_ranging__,  
            0xA:self.__decode_ul_alloc_start__}
        
        self._ext2_uiuc_decoder_map = {
            0x0:self.__decode_cqich_enhanced_alloc__,
            0x1:self.__decode_ho_anchor_active__,
            0x2:self.__decode_ho_active_anchor__,
            0x3:self.__decode_anchor_bs_switch__,
            0x4:self.__decode_ul_sounding_command__,
            0x5:self.__decode_ext_3_uiuc__,
            0x6:self.__decode_mimo_ul_enhanced__,
            0x7:self.__decode_harq_ulmapie__,
            0x8:self.__decode_harq_ackch_region_alloc__,
            0x9:self.__decode_mimo_ul_basic_ie__,
            0xA:self.__decode_mini_subchan_alloc_ie__,
            0xB:self.__decode_ul_pc_bitmap__,
            0xC:self.__decode_persistent_harq__,
            0xD:self.__decode_fdd_paired_alloc__,
            0xE:self.__decode_aas_sdma__,
            0xF:self.__decode_feedback_polling__,}
               
               
    def __decode_fastfeedback_allocation__(self,cid,bitstream):
        ulmapie = ULMapIEFastFeedBack()
        ulmapie.cid = cid
        ulmapie.symbol_offset = bitstream.read(8)
        ulmapie.subchannel_offset = bitstream.read(7)
        ulmapie.no_ofdma_symbols = bitstream.read(7)
        ulmapie.no_subchannels = bitstream.read(7)
        ulmapie.reserved = bitstream.read(3)
        self._ulmaphandler.OnReceiveULMAPIeFastFeedBack(ulmapie)
        
        
            
    def __decode_burst_profile__(self, cid,uiuc,bitstream):
        ulmapie = ULMapIEBurstProfile(uiuc)
        ulmapie.cid = cid
        ulmapie.duration = bitstream.read(10)
        ulmapie.repet_coding_ind.val = bitstream.read(2)
        if(self._aas_amc_zone_enabled == True):
            ulmapie.aas_amc_zone_mode = True
            ulmapie.slot_offset = bitstream.read(12)
        self._ulmaphandler.OnReceiveULMAPIeBurstProfile(ulmapie)
    

    def __decode_ranging_opportunity__(self,cid,bitstream):
        ulmapie = ULMapIECDMABRequestAndRanging()
        ulmapie.cid                                 = cid
        ulmapie.ofdma_symbol_offset                 = bitstream.read(8)
        ulmapie.subchannel_offset                   = bitstream.read(7)
        ulmapie.no_ofdma_symbols                    = bitstream.read(7)
        ulmapie.no_subchannels                      = bitstream.read(7)
        ulmapie.ranging_method.val                  = bitstream.read(2)
        ulmapie.dedicated_ranging_indicator.val     = bitstream.read(1)
        # fill the string information
        ulmapie.ranging_method.strinfo = ULMAPDict.GetRangingMethodInfo(ulmapie.ranging_method.val)        
        ulmapie.dedicated_ranging_indicator.strinfo = ULMAPDict.GetDedicatedRangingIndInfo(
                                                      ulmapie.dedicated_ranging_indicator.val)
        # Notify ULMapDistiller Handler
        self._ulmaphandler.OnReceiveULMAPIeRangingOpportunity(ulmapie)
    
    
    def __decode_cdma_alloc__(self,cid,bitstream):
        ulmapie = ULMapIECDMAAllocation()
        ulmapie.cid                         = cid
        ulmapie.duration.val                = bitstream.read(6)
        ulmapie.uiuc_transmission.val       = bitstream.read(4)
        ulmapie.repet_coding_ind.val        = bitstream.read(2)
        ulmapie.frame_number_index.val      = bitstream.read(4)
        ulmapie.ranging_code.val            = bitstream.read(8)
        ulmapie.ranging_symbol.val          = bitstream.read(8)
        ulmapie.ranging_subchannel.val      = bitstream.read(7)
        ulmapie.bw_req_mandatory.val        = bitstream.read(1)
        
        # Fill the string information
        ulmapie.repet_coding_ind.strinfo    = ULMAPDict.GetRepetCodingIndInfo(ulmapie.repet_coding_ind.val)        
        ulmapie.bw_req_mandatory.strinfo    = ULMAPDict.GetBwReqMandatoryInfo(ulmapie.bw_req_mandatory.val)
        
        # Notify Distiller Handler
        self._ulmaphandler.OnReceiveULMAPIeCDMAAllocation(ulmapie)


    def __decode_ext_uiuc__(self,cid,bitstream):
        ext_uiuc = bitstream.read(4)
        try:
            self._ext_uiuc_decoder_map[ext_uiuc](cid,bitstream)
        except KeyError, x:
            raise x


    def __reserved__(self,cid,bitstream):
        # read length
        length=bitstream.read(4)
        bitstream.read(length)

                
    def __decode_power_ctrl__(self,cid,bitstream):
        # ignore length
        bitstream.read(4)
        ulmapie = ULMapIEPowerControl()
        ulmapie.cid = cid
        ulmapie.power_control.val           = bitstream.read(8)
        ulmapie.power_measurement_frame.val = bitstream.read(8)
        self._ulmaphandler.OnReceivePowerControl(ulmapie)
    
    
    def __decode_aas_ul__(self,cid,bitstream): 
        #set the aas_amc_enabled flag to indicate that we are in aas zone
        self._aas_amc_zone_enabled = True        
        # ignore length
        bitstream.read(4)        
        ulmapie = ULMapIE_AAS_UL()
        ulmapie.cid = cid
        ulmapie.permutation.val         = bitstream.read(2)
        ulmapie.permutation.strinfo     = ULMAPDict.GetPermutationInfo(ulmapie.permutation.val)
        ulmapie.ul_perm_base.val        = bitstream.read(7)
        ulmapie.symbol_offset.val       = bitstream.read(8)
        ulmapie.aas_zone_length.val     = bitstream.read(8)
        ulmapie.ul_preamble_config.val  = bitstream.read(2)
        ulmapie.ul_preamble_config.strinfo = '%d symbol(s)' % ulmapie.ul_preamble_config.val
        ulmapie.reserved.val = bitstream.read(5)
        self._ulmaphandler.OnReceiveAAS_UL(ulmapie)
    
    
    def __decode_cqich_alloc__(self,uiuc,uiuc_ext,bitstream):
        ulmapie = ULMapIE_CQICHAllocation(bitstream.read(4))
        ulmapie.cqich_id.val = bitstream.read(GlobalStaticTable.cqich_id_size)
        ulmapie.alloc_offset.val = bitstream.read(6)
        ulmapie.period.val = bitstream.read(2)
        ulmapie.frame_offset.val = bitstream.read(3)
        ulmapie.duration.val = bitstream.read(3)
        ulmapie.report_conf_included.val = bitstream.read(1)
        if(int(ulmapie.report_conf_included) == 1):
            ulmapie.feedback_type.val = bitstream.read(2)
            ulmapie.report_type.val = bitstream.read(1)
            if(int(ulmapie.report_type) == 0):
                ulmapie.cinr_preamble_report_type.val = bitstream.read(1)
            else:
                ulmapie.zone_permutation.val = bitstream.read(3)
                ulmapie.zone_type.val = bitstream.read(2)
                ulmapie.zone.prbs_id.val = bitstream.read(2)
                if(int(ulmapie.zone_permutation) == 0x00 | int(ulmapie.zone_permutation) == 0x01):
                    ulmapie.major_group_ind.val = bitstream.read(1)
                    if(int(ulmapie.major_group_ind) == 1):
                        ulmapie.pusc_major_group_bitmap.val = bitstream.read(6)
                ulmapie.cinr_zone_meas_type.val = bitstream.read(1)
                
        if(int(ulmapie.feedback_type) == 0x00):
            ulmapie.averaging_param_incl.val = bitstream.read(1)
            if(int(ulmapie.averaging_param_incl) == 1):
                ulmapie.averaging_param.val = bitstream.read(4)
                
        self._ulmaphandler.OnReceiveCQICHAlloc(ulmapie)
    
    
    def __decode_ul_zone__(self,cid,bitstream): 

        # ignore length
        bitstream.read(4)        
        ulmapie = ULMapIEUplinkZone()
        ulmapie.cid = cid
        ulmapie.ofdma_symbol_offset.val = bitstream.read(7)
        ulmapie.permutation.val = bitstream.read(2)
        ulmapie.permutation.strinfo = ULMAPDict.GetPermutationInfo(ulmapie.permutation.val)
        ulmapie.ul_permbase.val = bitstream.read(7)
        ulmapie.amc_type.val = bitstream.read(2)
        if(ulmapie.permutation.val == 0x02):
            #set the aas_amc_enabled flag to indicate that we are now in amc zone
            self._aas_amc_zone_enabled = True                            
            ulmapie.amc_type.strinfo = ULMAPDict.GetAMCTypeInfo(ulmapie.amc_type.val)
        
        ulmapie.use_all_subch.val = bitstream.read(1)
        ulmapie.use_all_subch.strinfo = ULMAPDict.GetUseAllSubchIndInfo(ulmapie.use_all_subch.val)
        ulmapie.disable_subch_rotation.val = bitstream.read(1)
        ulmapie.hfdd_over_subframe_alloc = bitstream.read(1)
        ulmapie.hfdd_inter_ul_gap_alloc = bitstream.read(1)
        ulmapie.reserved.val = bitstream.read(2)
        self._ulmaphandler.OnReceiveULZone(ulmapie)
        
    
    def __decode_phy_mod__(self,cid,bitstream): 
        #ignore length
        bitstream.read(4)
        ulmapie = ULMapIEPhysicalModifier()
        ulmapie.cid = cid
        ulmapie.preamble_modifier_type.val = bitstream.read(1)
        if(ulmapie.preamble_modifier_type.val == 0):
            ulmapie.preamble_modifier_type.strinfo = 'Frequency-shifted preamble'
            ulmapie.preamble_frequency_shift_index.val = bitstream.read(4)
        else:
            ulmapie.preamble_modifier_type.strinfo = 'Timed-shifted preamble'
            ulmapie.preamble_time_shift_index.val = bitstream.read(4)
        
        ulmapie.pilot_pattern_modifier.val = bitstream.read(1)
        if(ulmapie.pilot_pattern_modifier.val == 0):
            ulmapie.pilot_pattern_modifier.strinfo = 'Not applied'
        else:
            ulmapie.pilot_pattern_modifier.strinfo = 'Applied'
            
        ulmapie.pilot_pattern_index.val = bitstream.read(2)
        ulmapie.pilot_pattern_index.strinfo = ULMAPDict.GetPilotPatternIdxInfo(ulmapie.pilot_pattern_index.val)
        self._ulmaphandler.OnReceivePhysicalModifier(ulmapie)

    
    def __decode_fast_tracking__(self,cid,bitstream): 
        ulmapie = ULMapIEFastTrackingIE(bitstream.read(4))
        ulmapie.map_index.val = bitstream.read(2)
        ulmapie.reserved.val = bitstream.read(6)
        for i in xrange(1,ulmapie.length):
            ulmapie.PushFastTrackingCorrection(bitstream.read(3), bitstream.read(3), bitstream.read(2))
        self._ulmaphandler.OnReceiveFastTracking(ulmapie)
    
    
    def __decode_ulpusc_burst_alloc__(self,cid,bitstream): 
        # ignore length
        bitstream.read(4)
        ulmapie = ULMapIE_ULPUSCBurstAlloc()
        ulmapie.uiuc.val = bitstream.read(4)
        ulmapie.segment.val = bitstream.read(2)
        ulmapie.ul_permbase.val = bitstream.read(7)
        ulmapie.ofdma_symbol_offset.val = bitstream.read(8)
        ulmapie.subchannel_offset.val = bitstream.read(6)
        ulmapie.duration.val = bitstream.read(10)
        ulmapie.repet_coding_ind.val = bitstream.read(2)
        ulmapie.repet_coding_ind.strinfo = ULMAPDict.GetRepetCodingIndInfo(int(ulmapie.repet_coding_ind))
        ulmapie.reserved.val = bitstream.read(1)
        self._ulmaphandler.OnReceiveULPUSCBurstAlloc(ulmapie)
    
    
    def __decode_fast_ranging__(self,cid,bitstream):
        ulmapie = ULMapIEFastRanging(bitstream.read(4))
        ulmapie.ho_id_ind.val = bitstream.read(1)
        if(int(ulmapie.ho_id_ind) == 0):
            ulmapie.ho_id_ind.strinfo = 'MAC Address is present'
            macaddr = bitstream.read(48)
            integerlist = IntegerToListInteger(macaddr,6,8)
            ulmapie.mac_address.strinfo = '%2.2X-%2.2X-%2.2X-%2.2X-%2.2X-%2.2X' % (
                                           integerlist[0],integerlist[1],integerlist[2],
                                           integerlist[3],integerlist[4],integerlist[5])
        else:
            ulmapie.ho_id_ind.strinfo = 'HO ID is present'
            ulmapie.ho_id.val = bitstream.read(8)
        ulmapie.uiuc.val = bitstream.read(4)
        ulmapie.duration.val = bitstream.read(10)
        ulmapie.repet_coding_ind.val = bitstream.read(2)
        ulmapie.repet_coding_ind.strinfo = ULMAPDict.GetRepetCodingIndInfo(int(ulmapie.repet_coding_ind))
        self._ulmaphandler.OnReceiveFastRanging(ulmapie)
    
    
    def __decode_ul_alloc_start__(self,cid,bitstream): 
        #ignore UL Alloc start
        bitstream.read(4)
        ulmapie = ULMapIEULAllocationStart()
        ulmapie.symbol_offset.val = bitstream.read(8)
        ulmapie.subchannel_offset.val = bitstream.read(7)
        ulmapie.hfdd_ul_subframe_ind.val = bitstream.read(1)
    
    
    def __decode_cqich_enhanced_alloc__(self,cid,bitstream): 
        pass
    
    
    def __decode_ho_anchor_active__(self,cid,bitstream): 
        pass
    
    
    def __decode_ho_active_anchor__(self,cid,bitstream): 
        pass
    
    
    def __decode_anchor_bs_switch__(self,cid,bitstream): 
        pass
    
    
    def __decode_ul_sounding_command__(self,cid,bitstream): 
        pass
    
    
    def __decode_ext_3_uiuc__(self,cid,bitstream): 
        pass
    
    
    def __decode_mimo_ul_enhanced__(self,cid,bitstream): 
        pass
    
    
    def __decode_harq_ulmapie__(self,cid,bitstream): 
        pass
    
    
    def __decode_harq_ackch_region_alloc__(self,cid,bitstream): 
        pass
    
    
    def __decode_mimo_ul_basic_ie__(self,cid,bitstream): 
        pass
    
    
    def __decode_mini_subchan_alloc_ie__(self,cid,bitstream): 
        pass
    
    
    def __decode_ul_pc_bitmap__(self,cid,bitstream): 
        pass
    
    
    def __decode_persistent_harq__(self,cid,bitstream): 
        pass
    
    
    def __decode_fdd_paired_alloc__(self,cid,bitstream): 
        pass
    
    
    def __decode_aas_sdma__(self,cid,bitstream): 
        pass
    
    
    def __decode_feedback_polling__(self,cid,bitstream): 
        pass
        
        
    def Decode(self,macheader, ulmap_pdu):
        assert(isinstance(ulmap_pdu,str))
        assert(len(ulmap_pdu) > 0)
        # reset the aas_amc flag
        self._aas_amc_zone_enabled = False        
        self._ulmaphandler.OnStartPDU(ulmap_pdu,macheader)        
        if(int(macheader.crc_indicator) == 0):
            bitstream = BitStream(ulmap_pdu[6:])
        else:
            bitstream = BitStream(ulmap_pdu[6:len(ulmap_pdu)-4])
            
        #ignore type message
        bitstream.read(8)
        ulmap = ULMap()
        ulmap.fdd_part_flag = bitstream.read(1)
        ulmap.reserved = bitstream.read(7)
        ulmap.ucd_count = bitstream.read(8)
        ulmap.alloc_start_time = bitstream.read(32)
        ulmap.no_ofdma_symbols = bitstream.read(8)
        #notify the ulmap handler
        self._ulmaphandler. OnReceiveULMAP(ulmap)
        
        # looping until bitstream is exhausted
        while True:
            try:
                cid_temp = bitstream.read(16)
                uiuc_temp = bitstream.read(4)
                if(uiuc_temp > 0 and uiuc_temp < 11):
                    self.__decode_burst_profile__(cid_temp,uiuc_temp,bitstream)
                else:
                    func = self._map_decoder_ie[uiuc_temp]
                    func(cid_temp,bitstream)
            except EOFError:
                break
        if(int(macheader.crc_indicator)==1):
            self._ulmaphandler.OnReceiveCRC(self._calc_crc,self._current_crc)
            
            

class DLMAPDecoder(Decoder):
    def __init__(self,handler):
        self._handler = handler
        self._ext_diuc_map = {0x0:self.__process_channel_measurement__,
                              0x1:self.__process_stc_zone__,
                              0x2:self.__process_aas_dl__,
                              0x3:self.__process_data_location_another_bs__,
                              0x4:self.__process_cid_switch_ie__,
                              0x5:lambda diuc,ext_diuc,bitstream :None,
                              0x6:lambda diuc,ext_diuc,bitstream:None,
                              0x7:self.__process_harq_map_pointer__,
                              0x8:self.__process_phymod_dl__,
                              0x9:lambda diuc,ext_diuc,bitstream:None,
                              0xA:self.__process_broadcast_ctrl_pointer__,
                              0xB:self.__process_dl_pusc_burst_alloc__,
                              0xC:self.__process_pusc_asca_alloc__,
                              0xD:self.__process_h_fdd_group_switch__,
                              0xE:self.__process_ext_broadcast_ctrl_pointer__,
                              0xF:self.__process_ul_interference_noise_level__}
        self._cid_switch_enable = False
    
    def __process_burst_profile__(self,diuc,bitstream):
        assert(isinstance(bitstream,BitStream))
        dlmapie = DLMapIEBurstProfiles()
        dlmapie.diuc = diuc
        if self._cid_switch_enable == True :
            n_cid = bitstream.read(8)
            while(n_cid > 0):
                #@todo: Next we should check whether the DLMAP is SUB-DL-UL-MAP
                dlmapie.cid_list.append(bitstream.read(16))
                n_cid -= 1
        dlmapie.ofdma_symbol_offset = bitstream.read(8)
        dlmapie.subchannel_offset = bitstream.read(6)
        dlmapie.boosting.val = bitstream.read(3)
        dlmapie.num_ofdma_symbols = bitstream.read(7)
        dlmapie.num_subchannels = bitstream.read(6)
        dlmapie.repet_coding_ind.val = bitstream.read(2)
        self._handler.OnReceiveDLMAPIeBurstProfile(dlmapie)
            

    def __process_extended_diuc__(self,diuc,bitstream):
        ext_diuc = bitstream.read(4)
        try:
            func = self._ext_diuc_map[ext_diuc]
            func(diuc,ext_diuc,bitstream)
        except KeyError:
            raise NotImplementedError, 'cannot process extended diuc 0x%X'%(ext_diuc)
            
     
    def __process_channel_measurement__(self,diuc,ext_diuc,bitstream):
        dlmapie = DLMapIEChannelMeasurement()
        dlmapie.diuc = diuc
        dlmapie.ext_diuc.val = ext_diuc
        dlmapie.ext_diuc.strinfo = DLMAPDict.GetDIUCExtInfo(dlmapie.ext_diuc.val)
        dlmapie.length = bitstream.read(4)
        dlmapie.channel_nr = bitstream.read(8)
        dlmapie.ofdma_symbol_offset = bitstream.read(8)
        dlmapie.cid = bitstream.read(16)
        self._handler.OnReceiveDLMAPIEChannelMeasurement(dlmapie)

 
    def __process_cid_switch_ie__(self,diuc,ext_diuc,bitstream):
        dlmapie = DLMapIECIDSwitch()
        dlmapie.diuc = diuc
        dlmapie.ext_diuc.val = ext_diuc
        dlmapie.ext_diuc.strinfo = DLMAPDict.GetDIUCExtInfo(dlmapie.ext_diuc.val)        
        dlmapie.length = bitstream.read(4)
        self._cid_switch_enable = not self._cid_switch_enable        
        self._handler.OnReceiveDLMAPIeCIDSwitch(dlmapie)

    
    def __process_stc_zone__(self,diuc,ext_diuc,bitstream):
        dlmapie                         = DLMapIE_STC_DLZone()
        dlmapie.diuc                    = diuc
        dlmapie.ext_diuc.val            = ext_diuc
        dlmapie.ext_diuc.strinfo = DLMAPDict.GetDIUCExtInfo(dlmapie.ext_diuc.val)        
        dlmapie.length                  = bitstream.read(4)
        dlmapie.ofdma_symbol_offset.val = bitstream.read(8)
        dlmapie.permutation.val         = bitstream.read(2)
        dlmapie.permutation.strinfo     = DLMAPDict.GetPermutationInfo(int(dlmapie.permutation))
        dlmapie.use_all_sc.val          = bitstream.read(1)
        dlmapie.use_all_sc.strinfo = DLMAPDict.GetSubchanUsageInfo(int(dlmapie.use_all_sc))
        dlmapie.stc.val                 = bitstream.read(2)
        dlmapie.stc.strinfo = DLMAPDict.GetSTCInfo(int(dlmapie.stc))
        dlmapie.matrix_ind.val          = bitstream.read(2)
        dlmapie.dl_permbase.val         = bitstream.read(5)
        dlmapie.prbs_id.val             = bitstream.read(2)
        dlmapie.amc_type.val            = bitstream.read(2)
        dlmapie.amc_type.strinfo = DLMAPDict.GetAMCTypeInfo(int(dlmapie.amc_type))
        dlmapie.midamble_presence.val   = bitstream.read(1)
        dlmapie.midamble_presence.strinfo = DLMAPDict.GetMidamblePresence(int(dlmapie.midamble_presence))
        dlmapie.midamble_boosting.val   = bitstream.read(1)
        dlmapie.midamble_boosting.strinfo = DLMAPDict.GetMidambleBoosting(int(dlmapie.midamble_boosting))
        dlmapie.antennas_2_3_select.val = bitstream.read(1)
        dlmapie.antennas_2_3_select.strinfo = DLMAPDict.GetAntennas_2_3_Select(int(dlmapie.antennas_2_3_select))
        dlmapie.dedicated_pilots.val    = bitstream.read(1)
        dlmapie.dedicated_pilots.strinfo = DLMAPDict.GetDedicatedPilots(int(dlmapie.dedicated_pilots))
        dlmapie.reserved.val            = bitstream.read(4)
        self._handler.OnReceiveSTC_Zone(dlmapie)
    
    
    def __process_aas_dl__(self,diuc,ext_diuc,bitstream):
        dlmapie = DLMapIE_AAS_DL()
        dlmapie.diuc                        = diuc
        dlmapie.ext_diuc.val                = ext_diuc
        dlmapie.ext_diuc.strinfo            = DLMAPDict.GetDIUCExtInfo(
                                              int(dlmapie.ext_diuc.val))        
        dlmapie.length                      = bitstream.read(4)
        dlmapie.ofdma_symbol_offset.val     = bitstream.read(8)
        dlmapie.permutation.val             = bitstream.read(3)
        dlmapie.permutation.strinfo         = DLMAPDict.GetPermAASDLInfo(
                                              int(dlmapie.permutation))
        dlmapie.dl_permbase.val             = bitstream.read(6)
        dlmapie.dl_preamble_config.val      = bitstream.read(2)
        dlmapie.dl_preamble_config.strinfo  = '%d symbols' %(int(dlmapie.dl_preamble_config))
        dlmapie.preamble_type.val           = bitstream.read(1)
        dlmapie.preamble_type.strinfo       = DLMAPDict.GetPreambleTypeInfo(
                                              int(dlmapie.preamble_type))
        dlmapie.preamble_type.strinfo       = DLMAPDict.GetPreambleTypeInfo(
                                              int(dlmapie.preamble_type))
        dlmapie.prbs_id.val                 = bitstream.read(2)
        dlmapie.diversity_map.val           = bitstream.read(1)
        dlmapie.diversity_map.strinfo       = DLMAPDict.GetDivMAPInfo(
                                              int(dlmapie.diversity_map))
        dlmapie.reserved                    = bitstream.read(1)
        self._handler.OnReceiveAASDL(dlmapie)
    
    
    def __process_data_location_another_bs__(self,diuc,ext_diuc,bitstream):
        dlmapie = DLMapIEDataLocAnotherBS()
        dlmapie.diuc                        = diuc
        dlmapie.ext_diuc.val                = ext_diuc
        dlmapie.ext_diuc.strinfo            = DLMAPDict.GetDIUCExtInfo(
                                              int(dlmapie.ext_diuc))        
        dlmapie.length                      = bitstream.read(4)
        dlmapie.segment.val                 = bitstream.read(2)
        dlmapie.used_subchannels.val        = bitstream.read(6)
        dlmapie.diuc                        = bitstream.read(4)
        dlmapie.frame_advance.val           = bitstream.read(3)
        dlmapie.reserved.val                = bitstream.read(1)
        dlmapie.ofdma_symbol_offset.val     = bitstream.read(8)
        dlmapie.subchannel_offset.val       = bitstream.read(6)
        dlmapie.boosting.val                = bitstream.read(3)
        dlmapie.boosting.strinfo            = DLMAPDict.GetBoostingInfo(
                                              int(dlmapie.boosting))
        dlmapie.preamble_index.val          = bitstream.read(7)
        dlmapie.no_ofdm_symbols.val         = bitstream.read(8)
        dlmapie.no_subchannels.val          = bitstream.read(6)
        dlmapie.repet_coding_ind.val        = bitstream.read(2)
        dlmapie.repet_coding_ind.strinfo    = DLMAPDict.GetRepetCodingIndInfo(
                                              int(dlmapie.repet_coding_ind))
        dlmapie.cid.val                     = bitstream.read(16)
        self._handler.OnReceiveDataLocationAnotherBS(dlmapie)
    
    
    def __process_harq_map_pointer__(self,diuc,ext_diuc,bistream):
        pass
    
    
    def __process_phymod_dl__(self,diuc,ext_diuc,bitstream): 
        dlmapie = DLMapIEPhysicalModifier()
        dlmapie.diuc                                    = diuc
        dlmapie.ext_diuc.val                            = ext_diuc
        dlmapie.ext_diuc.strinfo                        = DLMAPDict.GetDIUCExtInfo(
                                                          int(dlmapie.ext_diuc))
        dlmapie.length                                  = bitstream.read(4)
        dlmapie.preamble_modifier_type = bitstream.read(1)
        if(dlmapie.preamble_modifier_type == 0):
            dlmapie.preamble_frequency_shift_index.val  = bitstream.read(4)
        else:
            dlmapie.preamble_time_shift_index.val       = bitstream.read(4)
            
        dlmapie.pilot_pattern_modifier.val              = bitstream.read(1)
        dlmapie.pilot_pattern_modifier.strinfo          = DLMAPDict.GetPilotPatternModInfo(
                                                          int(dlmapie.pilot_pattern_modifier))
        dlmapie.pilot_pattern_index.val                 = bitstream.read(2)
        dlmapie.pilot_pattern_index.strinfo             = DLMAPDict.GetPilotPatternIdxInfo(
                                                          int(dlmapie.pilot_pattern_index))
        self._handler.OnReceivePhysicalModifier(dlmapie)
    
    
    def __process_broadcast_ctrl_pointer__(self,diuc,ext_diuc,bitstream):
        dlmapie = DLMapIEBroadcastCtrlPointer()
        dlmapie.diuc                                    = diuc
        dlmapie.ext_diuc.val                            = ext_diuc
        dlmapie.ext_diuc.strinfo                        = DLMAPDict.GetDIUCExtInfo(
                                                          int(dlmapie.ext_diuc))
        dlmapie.length                                  = bitstream.read(4)
        dlmapie.dcd_ucd_trans_frame.val = bitstream.read(7)
        dlmapie.skip_broadcast_sys_update = bitstream.read(1)
        if(int(dlmapie.skip_broadcast_sys_update) == 0 ):
            dlmapie.broadcast_sys_update_type.val = bitstream.read(1)
            dlmapie.broadcast_sys_update_type.strinfo = DLMAPDict.GetBroadcastSysUpdInfo(
                                                        int(dlmapie.broadcast_sys_update_type))
            dlmapie.broadcast_sys_update_trans_frame.val = bitstream.read(7)        
        self._handler.OnReceiveBroadcastCtrlPointer(dlmapie) 
        
    
    
    def __process_dl_pusc_burst_alloc__(self,diuc,ext_diuc,bitstream): 
        pass
    
    
    def __process_pusc_asca_alloc__(self,diuc,ext_diuc,bitstream): 
        pass
    
    
    def __process_h_fdd_group_switch__(self,diuc,ext_diuc,bitstream): 
        pass
    
    
    def __process_ext_broadcast_ctrl_pointer__(self,diuc,ext_diuc,bitstream): 
        pass
    
    
    def __process_ul_interference_noise_level__ (self,diuc,ext_diuc,bitstream): 
        pass
           
        
    def Decode(self,macheader,payload):
        assert (isinstance(payload,str))
        assert(isinstance(macheader,MACHeaderGeneric))
        self._handler.OnStartPDU(payload,macheader)
        iterdata = iter(payload[6:])
        #ignore message type
        iterdata.next()
        dlmap = DLMap()
        dlmap.frame_duration_code = ord(iterdata.next())
        dlmap.frame_number = (ord(iterdata.next()) << 16 |
                              ord(iterdata.next()) << 8 |
                              ord(iterdata.next())) 
        dlmap.dcd_count = ord(iterdata.next())
        dlmap.bsid.strval = ("%2.2X-%2.2X-%2.2X-%2.2X-%2.2X-%2.2X" % 
                             (ord(iterdata.next()),
                             ord(iterdata.next()),
                             ord(iterdata.next()),
                             ord(iterdata.next()),
                             ord(iterdata.next()),
                             ord(iterdata.next())))
        dlmap.num_ofdma_symbols = ord(iterdata.next())
        self._handler.OnReceiveDLMAP(dlmap)
        
        if(int(macheader.crc_indicator) == 0):
            bitstream = BitStream(payload[19:])
        else:
            bitstream = BitStream(payload[19:len(payload)-4])        
        while True:
            try:
                diuc_temp = bitstream.read(4)
                if(diuc_temp < 13):
                    self.__process_burst_profile__(diuc_temp,bitstream)

                
                elif diuc_temp == 14:
                    pass
                
                elif diuc_temp == 15:
                    self.__process_extended_diuc__(diuc_temp, bitstream)
                    
            except EOFError:
                break

        if(int(macheader.crc_indicator)==1):
            self._handler.OnReceiveCRC(self._calc_crc,self._current_crc)
                    
        self._handler.OnEndPDU()
        
        

class UCDDecoder(Decoder):
    SIZE_OF_CQICH_ID    = 176
    
    def __init__(self,handler):
        super(UCDDecoder,self).__init__()
        assert(isinstance(handler, UCDHandler))
        self._handler = handler


    def __decode_ulburstprofile__(self,tlv):
        uiuc = ord(tlv.Value.GetValueAsString()[0:1]) & 0x0F
        ulburstprofile = UCD.ULBurstProfile(uiuc)
        ulburstprofile.reserved = ord(tlv.Value.GetValueAsString()[0:1]) & 0xF0
        ulburstprofile.type     = tlv.Type
        ulburstprofile.length   = tlv.Length
        ulburstprofile.tlv_list = TLVDecoder(tlv.Value.GetValueAsString()[1:]).Decode()
        self._handler.OnReceiveULBurstProfile(ulburstprofile)
        
    def __store_tlv_param__(self,tlv):
        if(int(tlv.Type) == UCDDecoder.SIZE_OF_CQICH_ID):
            GlobalStaticTable.cqich_id_size = int(tlv.Value)
        
    def Decode(self,macheader,ucdpayload):
        if(len(ucdpayload) < 6 ):
            raise RuntimeError, 'Invalid UCD Payload'
        #first we extract basic parameters and ignore message type
        self._handler.OnStartPDU(ucdpayload,macheader)        
        iterbyte = iter(ucdpayload[6:])
        iterbyte.next()
        
        ucd = UCD()
       
        ucd.configuration_change_count  = ord(iterbyte.next())
        ucd.ranging_back_off_start      = ord(iterbyte.next())
        ucd.ranging_back_off_end        = ord(iterbyte.next())
        ucd.request_back_off_start      = ord(iterbyte.next())
        ucd.request_back_off_end        = ord(iterbyte.next())
        self._handler.OnReceiveUCD(ucd)
        
        #now parse all TLV
        if(int(macheader.crc_indicator) == 0):
            tlvlist = TLVDecoder(ucdpayload[12:]).Decode()
        else:
            tlvlist = TLVDecoder(ucdpayload[12:len(ucdpayload)-4]).Decode()
            
        for tlv in tlvlist:
            if (tlv.Type.val == 1 ):
                self.__decode_ulburstprofile__(tlv)
            else:
                # store some of tlv value, for other decoding purposes (ULMAP Decoder)
                self.__store_tlv_param__(tlv)
                
                if tlv.Type.val in UCDDict.tlv_type:
                    tlv.Type.strinfo = UCDDict.tlv_type[tlv.Type.val]
                else:
                    tlv.Type.strinfo = 'Unknown'
                self._handler.OnReceiveULChannelEncoding(tlv)
        
        if(int(macheader.crc_indicator)==1):
            self._handler.OnReceiveCRC(self._calc_crc,self._current_crc)        
        self._handler.OnEndPDU()
    
   
   
class DCDDecoder(Decoder):
    def __init__(self,handler):
        self._handler = handler
        self._channel_encoding_valinfo = {
            2:lambda x:('%d in dBm' % int(x)),
            7:lambda x:('%d PSs'% int(x)),
            8:lambda x:('%d PSs'% int(x)),
            9:lambda x:('%d in dBm' % int(x)),
            13:self.__get_bsid_info__,
            }
        
    def __get_bsid_info__(self,val):
        pass
    
    
    def __decode_dlburstprofile__(self,tlv):
        diuc = ord(tlv.Value.GetValueAsString()[0:1]) & 0x0F
        dlburstprofile = DCD.DLBustProfile(diuc)
        dlburstprofile.reserved = ord(tlv.Value.GetValueAsString()[0:1]) & 0x0F
        dlburstprofile.type = tlv.Type
        dlburstprofile.length = tlv.Length
        dlburstprofile.tlv_list = TLVDecoder(tlv.Value.GetValueAsString()[1:]).Decode()
        self._handler.OnReceiveBurstProfile(dlburstprofile)
    
    
    def Decode(self,macheader,payload):
        if(len(payload) < 3 ):
            raise RuntimeError,'Invalid DCD Payload'
        self._handler.OnStartPDU(payload,macheader)
        
        #extract and ignore message type
        iterbyte = iter(payload[6:])
        iterbyte.next()
        dcd = DCD()
        dcd.reserved = ord(iterbyte.next())
        dcd.conf_change_count = ord(iterbyte.next())
        self._handler.OnReceiveDCD(dcd)
        
        #now parse all TLV
        if(int(macheader.crc_indicator) == 0):
            tlvlist = TLVDecoder(payload[9:]).Decode()
        else:
            tlvlist = TLVDecoder(payload[9:len(payload)-4]).Decode()
            
        for tlv in tlvlist:
            if int(tlv.Type) == 1 :
                self.__decode_dlburstprofile__(tlv)
            else:
                tlv.Type.strinfo = DCDDict.GetTypeInfo(tlv.Type.val)
                self._handler.OnReceiveChannelEncoding(tlv)

        if(int(macheader.crc_indicator)==1):
            self._handler.OnReceiveCRC(self._calc_crc,self._current_crc)
                            
        self._handler.OnEndPDU()



class RangingRequestDecoder(Decoder):
    def __init__(self,handler):
        assert(isinstance(handler, RangingRequestHandler))
        self._handler = handler
        
    
    def Decode(self,macheader,payload):
        if(len(payload < 2 )):
            raise PDUMMError('Invalid Ranging Request Payload')
        
        self._handler.OnStartPDU(payload,macheader)
        
        # Extract management message and 1 byte reserved payload
        iterbyte = iter(payload[6:])
        iterbyte.next()
        iterbyte.next()
        
        # parse all tlv
        if(int(macheader.crc_indicator) == 0):
            tlvlist = TLVDecoder(payload[8:]).Decode()
        else:
            tlvlist = TLVDecoder(payload[8:len(payload)-4]).Decode()
            
        # Iterate  tlv list
        for tlv in tlvlist:
            self._handler.OnReceiveTlvEncodedInfo(tlv)
        